package com.zhuhjay.framework.security.service;

import cn.hutool.core.lang.Assert;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.zhuhjay.common.base.exception.BasicBusinessException;
import com.zhuhjay.common.base.util.JacksonUtils;
import com.zhuhjay.common.core.util.IpAddressUtils;
import com.zhuhjay.common.core.util.JwtUtils;
import com.zhuhjay.common.redis.util.RedisUtils;
import com.zhuhjay.common.security.LoginUserDetail;
import com.zhuhjay.common.web.util.ServletUtils;
import com.zhuhjay.framework.security.SpecificUserInfo;
import com.zhuhjay.service.system.model.dto.UserLoginDto;
import com.zhuhjay.service.system.model.entity.SystemUser;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;

import static com.zhuhjay.common.base.constant.CacheConstant.*;
import static com.zhuhjay.common.base.constant.SecurityConstant.*;

/**
 * 用户登录请求处理业务
 * @author ZhuHJay
 * @date 2023/4/24 14:41
 */
@Service
public class LoginService {

    @Resource
    private AuthenticationManager manager;

    public void login(UserLoginDto loginDto) {
        Assert.notNull(loginDto, () -> new BasicBusinessException("请输入用户名密码"));
        Assert.notBlank(loginDto.getUsername(), () -> new BasicBusinessException("用户名不能为空"));
        Assert.notBlank(loginDto.getPassword(), () -> new BasicBusinessException("密码不能为空"));

        Authentication authentication;
        try {
            UsernamePasswordAuthenticationToken token =
                    new UsernamePasswordAuthenticationToken(loginDto.getUsername(), loginDto.getPassword());
            authentication = manager.authenticate(token);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                throw new BasicBusinessException("用户名或密码错误");
            } else {
                throw new BasicBusinessException(e.getMessage());
            }
        }

        // 获取当前登录的用户信息
        LoginUserDetail loginUserDetail = (LoginUserDetail) authentication.getPrincipal();
        // 执行登录后的处理
        afterLoginHandler(loginUserDetail);
    }

    private void afterLoginHandler(LoginUserDetail loginUserDetail) {
        // 生成token信息
        String token = JwtUtils.createToken(loginUserDetail);

        // 设置用户的登录信息
        HttpServletRequest request = ServletUtils.currentRequest();
        UserAgent userAgent = UserAgentUtil.parse(ServletUtil.getHeader(request, "User-Agent", StandardCharsets.UTF_8));
        loginUserDetail.setIp(ServletUtil.getClientIP(request));
        loginUserDetail.setLocation(IpAddressUtils.getRealAddressByIp(loginUserDetail.getIp()));
        loginUserDetail.setBrowser(userAgent.getBrowser().getName());
        loginUserDetail.setOs(userAgent.getOs().getName());

        // 将 token -> uid 放置缓存中
        RedisUtils.setCacheAndExpire(TOKEN_PREFIX + token, String.valueOf(loginUserDetail.getUid()),
                JWT_EXPIRATION_TIME, JWT_EXPIRATION_TIME_UNIT);
        // 将 uid -> userTokenInfo 放置缓存中
        RedisUtils.setCacheAndExpire(USER_UNIQUE_ID_PREFIX + loginUserDetail.getUid(),
                JacksonUtils.writeValueAsString(loginUserDetail), JWT_EXPIRATION_TIME, JWT_EXPIRATION_TIME_UNIT);
        // 用户在线缓存记录
        RedisUtils.addValue2Set(USER_ONLINE_PREFIX + loginUserDetail.getUsername(), String.valueOf(loginUserDetail.getUid()));

        // 返回token
        ServletUtils.currentResponse().setHeader(AUTH_HEADER_KEY, JwtUtils.takeRespAuth(token));
    }

    public void authLogin(SystemUser systemUser) {
        // 执行登录后的处理
        afterLoginHandler(new LoginUserDetail(SpecificUserInfo.of(systemUser)));
    }
}
